"""
模拟券商

用于测试和开发的模拟券商实现。
"""

import asyncio
import random
import uuid
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Dict, List, Optional, Any

from .base_broker import (
    BaseBroker, BrokerType, Order, OrderRequest, OrderStatus, OrderType, OrderSide,
    Trade, Position, AccountInfo, MarketData
)


class MockBroker(BaseBroker):
    """模拟券商"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(BrokerType.MOCK, config)
        
        # 模拟数据
        self.orders: Dict[str, Order] = {}
        self.trades: Dict[str, Trade] = {}
        self.positions: Dict[str, Position] = {}
        self.market_data: Dict[str, MarketData] = {}
        
        # 账户信息
        self.account_info = AccountInfo(
            account_id="MOCK_ACCOUNT_001",
            total_value=Decimal('1000000'),  # 100万初始资金
            available_cash=Decimal('1000000'),
            market_value=Decimal('0'),
            buying_power=Decimal('1000000'),
            unrealized_pnl=Decimal('0'),
            realized_pnl=Decimal('0')
        )
        
        # 模拟配置
        self.commission_rate = Decimal(str(config.get('commission_rate', 0.0003)))  # 万三手续费
        self.min_commission = Decimal(str(config.get('min_commission', 5)))        # 最低5元
        self.slippage = Decimal(str(config.get('slippage', 0.001)))               # 0.1%滑点
        self.fill_probability = config.get('fill_probability', 0.95)              # 95%成交概率
        self.partial_fill_probability = config.get('partial_fill_probability', 0.1) # 10%部分成交概率
        
        # 模拟市场数据
        self._init_mock_market_data()
        
        # 订阅的股票
        self.subscribed_symbols: set = set()
        
        # 模拟任务
        self._market_data_task: Optional[asyncio.Task] = None
        self._order_processing_task: Optional[asyncio.Task] = None
    
    def _init_mock_market_data(self):
        """初始化模拟市场数据"""
        # 一些常见股票的模拟数据
        mock_symbols = [
            '000001.SZ', '000002.SZ', '000858.SZ', '002415.SZ',
            '600000.SH', '600036.SH', '600519.SH', '600887.SH',
            '000300.SH', '510050.SH', '510300.SH', '159919.SZ'
        ]
        
        for symbol in mock_symbols:
            base_price = random.uniform(10, 200)
            self.market_data[symbol] = MarketData(
                symbol=symbol,
                last_price=Decimal(str(round(base_price, 2))),
                bid_price=Decimal(str(round(base_price * 0.999, 2))),
                ask_price=Decimal(str(round(base_price * 1.001, 2))),
                bid_size=random.randint(100, 10000),
                ask_size=random.randint(100, 10000),
                volume=random.randint(10000, 1000000),
                timestamp=datetime.now()
            )
    
    async def connect(self) -> bool:
        """连接券商"""
        try:
            # 模拟连接延迟
            await asyncio.sleep(0.1)
            
            self.is_connected = True
            
            # 启动模拟任务
            self._market_data_task = asyncio.create_task(self._simulate_market_data())
            self._order_processing_task = asyncio.create_task(self._process_orders())
            
            self.logger.info("模拟券商连接成功")
            return True
            
        except Exception as e:
            self.logger.error(f"模拟券商连接失败: {e}")
            return False
    
    async def disconnect(self):
        """断开连接"""
        self.is_connected = False
        
        # 停止模拟任务
        if self._market_data_task:
            self._market_data_task.cancel()
        if self._order_processing_task:
            self._order_processing_task.cancel()
        
        self.logger.info("模拟券商已断开连接")
    
    async def submit_order(self, order_request: OrderRequest) -> Order:
        """提交订单"""
        if not self.is_connected:
            raise RuntimeError("券商未连接")
        
        # 生成订单ID
        order_id = f"MOCK_{uuid.uuid4().hex[:8].upper()}"
        
        # 创建订单
        order = Order(
            order_id=order_id,
            client_order_id=order_request.client_order_id,
            symbol=order_request.symbol,
            side=order_request.side,
            order_type=order_request.order_type,
            quantity=order_request.quantity,
            price=order_request.price,
            stop_price=order_request.stop_price,
            status=OrderStatus.SUBMITTED
        )
        
        # 基本验证
        if order_request.side == OrderSide.BUY:
            # 检查资金是否充足
            required_cash = self._calculate_required_cash(order_request)
            if required_cash > self.account_info.available_cash:
                order.status = OrderStatus.REJECTED
                order.error_message = "资金不足"
                self.logger.warning(f"订单 {order_id} 被拒绝: 资金不足")
        else:
            # 检查持仓是否充足
            position = self.positions.get(order_request.symbol)
            if not position or position.quantity < order_request.quantity:
                order.status = OrderStatus.REJECTED
                order.error_message = "持仓不足"
                self.logger.warning(f"订单 {order_id} 被拒绝: 持仓不足")
        
        # 保存订单
        async with self._lock:
            self.orders[order_id] = order
        
        # 通知订单更新
        await self._notify_order_update(order)
        
        self.logger.info(f"订单已提交: {order_id}")
        return order
    
    async def cancel_order(self, order_id: str) -> bool:
        """撤销订单"""
        async with self._lock:
            order = self.orders.get(order_id)
            if not order:
                return False
            
            if order.is_active:
                order.status = OrderStatus.CANCELLED
                order.updated_time = datetime.now()
                
                # 通知订单更新
                await self._notify_order_update(order)
                
                self.logger.info(f"订单已撤销: {order_id}")
                return True
            
            return False
    
    async def get_order(self, order_id: str) -> Optional[Order]:
        """获取订单信息"""
        return self.orders.get(order_id)
    
    async def get_orders(self, symbol: Optional[str] = None) -> List[Order]:
        """获取订单列表"""
        orders = list(self.orders.values())
        if symbol:
            orders = [order for order in orders if order.symbol == symbol]
        return sorted(orders, key=lambda x: x.created_time or datetime.min, reverse=True)
    
    async def get_trades(self, symbol: Optional[str] = None) -> List[Trade]:
        """获取成交记录"""
        trades = list(self.trades.values())
        if symbol:
            trades = [trade for trade in trades if trade.symbol == symbol]
        return sorted(trades, key=lambda x: x.timestamp, reverse=True)
    
    async def get_positions(self) -> List[Position]:
        """获取持仓信息"""
        # 更新持仓市值和盈亏
        for position in self.positions.values():
            market_data = self.market_data.get(position.symbol)
            if market_data:
                position.market_value = Decimal(str(position.quantity)) * market_data.last_price
                position.unrealized_pnl = position.market_value - (Decimal(str(position.quantity)) * position.avg_cost)
        
        return list(self.positions.values())
    
    async def get_account_info(self) -> AccountInfo:
        """获取账户信息"""
        # 更新账户信息
        positions = await self.get_positions()
        
        total_market_value = sum(pos.market_value for pos in positions)
        total_unrealized_pnl = sum(pos.unrealized_pnl for pos in positions)
        
        self.account_info.market_value = total_market_value or Decimal('0')
        self.account_info.unrealized_pnl = total_unrealized_pnl or Decimal('0')
        self.account_info.total_value = self.account_info.available_cash + total_market_value
        
        return self.account_info
    
    async def get_market_data(self, symbol: str) -> Optional[MarketData]:
        """获取市场数据"""
        return self.market_data.get(symbol)
    
    async def subscribe_market_data(self, symbols: List[str]) -> bool:
        """订阅市场数据"""
        self.subscribed_symbols.update(symbols)
        
        # 为新订阅的股票生成模拟数据
        for symbol in symbols:
            if symbol not in self.market_data:
                base_price = random.uniform(10, 200)
                self.market_data[symbol] = MarketData(
                    symbol=symbol,
                    last_price=Decimal(str(round(base_price, 2))),
                    bid_price=Decimal(str(round(base_price * 0.999, 2))),
                    ask_price=Decimal(str(round(base_price * 1.001, 2))),
                    bid_size=random.randint(100, 10000),
                    ask_size=random.randint(100, 10000),
                    volume=random.randint(10000, 1000000),
                    timestamp=datetime.now()
                )
        
        self.logger.info(f"已订阅市场数据: {symbols}")
        return True
    
    async def unsubscribe_market_data(self, symbols: List[str]) -> bool:
        """取消订阅市场数据"""
        self.subscribed_symbols.difference_update(symbols)
        self.logger.info(f"已取消订阅市场数据: {symbols}")
        return True
    
    def _calculate_required_cash(self, order_request: OrderRequest) -> Decimal:
        """计算所需资金"""
        if order_request.order_type == OrderType.MARKET:
            # 市价单使用当前价格估算
            market_data = self.market_data.get(order_request.symbol)
            if market_data:
                price = market_data.ask_price if order_request.side == OrderSide.BUY else market_data.bid_price
            else:
                price = Decimal('100')  # 默认价格
        else:
            price = order_request.price or Decimal('100')
        
        amount = Decimal(str(order_request.quantity)) * (price or Decimal('100'))
        commission = max(amount * self.commission_rate, self.min_commission)
        
        return amount + commission
    
    async def _simulate_market_data(self):
        """模拟市场数据更新"""
        while self.is_connected:
            try:
                # 更新订阅的股票数据
                for symbol in self.subscribed_symbols:
                    if symbol in self.market_data:
                        await self._update_market_data(symbol)
                
                # 通知市场数据更新
                for symbol in self.subscribed_symbols:
                    if symbol in self.market_data:
                        await self._notify_market_data(self.market_data[symbol])
                
                await asyncio.sleep(1)  # 每秒更新一次
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"模拟市场数据更新错误: {e}")
                await asyncio.sleep(1)
    
    async def _update_market_data(self, symbol: str):
        """更新单个股票的市场数据"""
        market_data = self.market_data[symbol]
        
        # 模拟价格变动（随机游走）
        change_pct = random.gauss(0, 0.01)  # 1%标准差的正态分布
        new_price = market_data.last_price * (Decimal('1') + Decimal(str(change_pct)))
        new_price = max(Decimal('0.01'), new_price)  # 价格不能为负
        
        # 更新数据
        market_data.last_price = round(new_price, 2)
        market_data.bid_price = round(new_price * Decimal('0.999'), 2)
        market_data.ask_price = round(new_price * Decimal('1.001'), 2)
        market_data.bid_size = random.randint(100, 10000)
        market_data.ask_size = random.randint(100, 10000)
        market_data.volume = (market_data.volume or 0) + random.randint(0, 1000)
        market_data.timestamp = datetime.now()
    
    async def _process_orders(self):
        """处理订单"""
        while self.is_connected:
            try:
                # 获取活跃订单
                active_orders = [order for order in self.orders.values() if order.is_active]
                
                for order in active_orders:
                    await self._try_fill_order(order)
                
                await asyncio.sleep(0.5)  # 每0.5秒处理一次
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"订单处理错误: {e}")
                await asyncio.sleep(1)
    
    async def _try_fill_order(self, order: Order):
        """尝试成交订单"""
        market_data = self.market_data.get(order.symbol)
        if not market_data:
            return
        
        # 判断是否应该成交
        if random.random() > self.fill_probability:
            return
        
        # 确定成交价格
        if order.order_type == OrderType.MARKET:
            # 市价单
            if order.side == OrderSide.BUY:
                fill_price = (market_data.ask_price or market_data.last_price) * (Decimal('1') + self.slippage)
            else:
                fill_price = (market_data.bid_price or market_data.last_price) * (Decimal('1') - self.slippage)
        else:
            # 限价单
            if order.side == OrderSide.BUY:
                ask_price = market_data.ask_price or market_data.last_price
                if ask_price <= (order.price or Decimal('0')):
                    fill_price = order.price or ask_price
                else:
                    return  # 价格不满足
            else:
                bid_price = market_data.bid_price or market_data.last_price
                if bid_price >= (order.price or Decimal('999999')):
                    fill_price = order.price or bid_price
                else:
                    return  # 价格不满足
        
        # 确定成交数量
        remaining_qty = order.remaining_quantity
        if random.random() < self.partial_fill_probability and remaining_qty > 100:
            # 部分成交
            fill_qty = random.randint(100, remaining_qty)
        else:
            # 全部成交
            fill_qty = remaining_qty
        
        # 创建成交记录
        trade_id = f"TRADE_{uuid.uuid4().hex[:8].upper()}"
        commission = max(Decimal(str(fill_qty)) * (fill_price or Decimal('0')) * self.commission_rate, self.min_commission)
        
        trade = Trade(
            trade_id=trade_id,
            order_id=order.order_id,
            symbol=order.symbol,
            side=order.side,
            quantity=fill_qty,
            price=fill_price or Decimal('0'),
            commission=commission,
            timestamp=datetime.now()
        )
        
        # 更新订单状态
        async with self._lock:
            order.filled_quantity += fill_qty
            if order.avg_fill_price is None:
                order.avg_fill_price = fill_price
            else:
                # 计算平均成交价
                total_amount = ((order.avg_fill_price or Decimal('0')) * Decimal(str(order.filled_quantity - fill_qty)) +
                               (fill_price or Decimal('0')) * Decimal(str(fill_qty)))
                order.avg_fill_price = total_amount / Decimal(str(order.filled_quantity))
            
            order.commission += commission
            order.updated_time = datetime.now()
            
            if order.filled_quantity >= order.quantity:
                order.status = OrderStatus.FILLED
            else:
                order.status = OrderStatus.PARTIAL_FILLED
            
            # 保存成交记录
            self.trades[trade_id] = trade
            
            # 更新持仓和资金
            await self._update_position_and_cash(trade)
        
        # 通知更新
        await self._notify_order_update(order)
        await self._notify_trade(trade)
        
        self.logger.info(f"订单成交: {order.order_id}, 数量: {fill_qty}, 价格: {fill_price}")
    
    async def _update_position_and_cash(self, trade: Trade):
        """更新持仓和资金"""
        symbol = trade.symbol
        
        if trade.side == OrderSide.BUY:
            # 买入
            if symbol in self.positions:
                # 更新现有持仓
                position = self.positions[symbol]
                total_cost = position.avg_cost * Decimal(str(position.quantity)) + trade.price * Decimal(str(trade.quantity))
                position.quantity += trade.quantity
                position.avg_cost = total_cost / Decimal(str(position.quantity))
            else:
                # 新建持仓
                self.positions[symbol] = Position(
                    symbol=symbol,
                    quantity=trade.quantity,
                    avg_cost=trade.price,
                    market_value=trade.price * Decimal(str(trade.quantity)),
                    unrealized_pnl=Decimal('0')
                )
            
            # 减少可用资金
            self.account_info.available_cash -= trade.total_amount + trade.commission
            
        else:
            # 卖出
            if symbol in self.positions:
                position = self.positions[symbol]
                position.quantity -= trade.quantity
                
                # 计算已实现盈亏
                realized_pnl = (trade.price - position.avg_cost) * Decimal(str(trade.quantity)) - trade.commission
                position.realized_pnl += realized_pnl
                self.account_info.realized_pnl += realized_pnl
                
                # 如果持仓为0，删除持仓记录
                if position.quantity <= 0:
                    del self.positions[symbol]
            
            # 增加可用资金
            self.account_info.available_cash += trade.total_amount - trade.commission